﻿using Autofac;
using Catastrophe.Initialize.IOCProvier;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Emgu.CV;
using Emgu.CV.Cuda;
using Emgu.CV.CvEnum;
using Emgu.CV.Dpm;
using Emgu.CV.Features2D;
using Emgu.CV.Models;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Wpf.Ui;
using Wpf.Ui.Controls;

namespace Catastrophe.Emgucv.ViewMode
{
    /// <summary>
    /// 
    /// </summary>
    public partial class ucEmgucvModel : ObservableObject
    {
        /// <summary>
        /// 打开图片
        /// </summary>
        public ICommand OpenFile { get; set; }
        /// <summary>
        /// 保存复制的图片
        /// </summary>
        public ICommand SaveCopyImage { get; set; }
        /// <summary>
        /// 图片操作
        /// </summary>
        public ICommand ImageCv { get; set; }

        /// <summary>
        /// 显示的图片
        /// </summary>
        [ObservableProperty]
        private BitmapSource imageBrush1;
        /// <summary>
        /// 
        /// </summary>
        [ObservableProperty]
        private BitmapSource imageBrush2;
        /// <summary>
        /// 
        /// </summary>
        [ObservableProperty]
        private BitmapSource imageBrush3;
        /// <summary>
        /// 
        /// </summary>
        private Mat fileMat1 { get; set; }
        private Mat fileMat2 { get; set; }

        /// <summary>
        /// 消息弹出
        /// </summary>
        public ISnackbarService snackbarService { get; set; }
        /// <summary>
        /// 是否含有文件
        /// </summary>
        private bool isHasFile { get; set; }
        /// <summary>
        /// 保存的位置
        /// </summary>
        private string savePath = "SaveImage\\";
        public ucEmgucvModel()
        {
            OpenFile = new RelayCommand<string>(OpenFileMethod);
            ImageCv = new RelayCommand<string>(ImageCvMethod);
            SaveCopyImage = new RelayCommand(SaveCopyImageMethod);
            var ico = CatastropheProvider.GetIContainer("Catastrophe.Emgucv");
            snackbarService = ico.Resolve<ISnackbarService>();
        }

        private void SaveCopyImageMethod()
        {
            if (!Directory.Exists(savePath))
            {
                Directory.CreateDirectory(savePath);
            }
            var image = Clipboard.GetImage();
            if (image != null)
            {
                var mat= BitmapSourceExtension.ToMat(image);
                mat.Save(savePath + DateTime.Now.ToFileTime() + ".png");
            }
        }

        private void ImageCvMethod(string? type)
        {
            Mat dstmat;
            try
            {
                switch (type)
                {
                    case "1":
                        //灰度
                        dstmat = new Mat();
                        CvInvoke.CvtColor(fileMat1, dstmat, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);
                        ToImage3(dstmat);
                        break;
                    case "2":
                        //二值化
                        dstmat = new Mat();
                        CvInvoke.Threshold(fileMat1, dstmat, 40, 255, Emgu.CV.CvEnum.ThresholdType.Binary);
                        ToImage3(dstmat);
                        break;
                    case "3":
                        //灰度+二值化
                        dstmat = new Mat();
                        CvInvoke.CvtColor(fileMat1, dstmat, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);
                        CvInvoke.Threshold(dstmat, dstmat, 40, 255, Emgu.CV.CvEnum.ThresholdType.Binary);
                        ToImage3(dstmat);
                        break;
                    case "4":
                        var mat = ProcessImage(fileMat1);
                        ToImage3(mat);
                        break;
                        case "5":
                        PedestrianDetection();
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                snackbarService.Show("错误", ex.Message, Wpf.Ui.Controls.ControlAppearance.Secondary, new SymbolIcon(SymbolRegular.Fluent24), TimeSpan.FromSeconds(3));
            }

        }

        private void OpenFileMethod(string? type)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Filter = "所有文件 (*.*)|*.*", // 设置文件类型过滤器
                InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), // 初始目录
                Multiselect = false // 是否允许多选
            };
            var opendst = openFileDialog.ShowDialog();
            if (opendst == true)
            {
                isHasFile = true;
                var path = openFileDialog.FileName;
                if (type == "1")
                {
                    fileMat1 = new Mat(path);
                    ToImage1(fileMat1);
                }
                if (type == "2")
                {
                    fileMat2 = new Mat(path);
                    ToImage2(fileMat2);
                }
            }
        }
        private void ToImage1(Mat mat)
        {
            ImageBrush1 = Emgu.CV.BitmapSourceExtension.ToBitmapSource(mat);
        }
        private void ToImage2(Mat mat)
        {
            ImageBrush2 = Emgu.CV.BitmapSourceExtension.ToBitmapSource(mat);
        }
        private void ToImage3(Mat mat)
        {
            ImageBrush3 = Emgu.CV.BitmapSourceExtension.ToBitmapSource(mat);
        }

        /// <summary>
        /// 行人检测
        /// </summary>
        private void PedestrianDetection()
        {
            var dst = new Mat();
            using (Emgu.CV.Models.PedestrianDetector detector = new PedestrianDetector())
            {
                detector.Init();
                String text = detector.ProcessAndRender(fileMat1, dst);
            }
            ToImage3(dst);
        }
        /// <summary>
        /// 形状（三角形、矩形、圆形、线条）检测
        /// </summary>
        /// <param name="img"></param>
        /// <returns></returns>
        public Mat ProcessImage(Mat img)
        {
            using (UMat gray = new UMat())
            using (UMat cannyEdges = new UMat())
            using (Mat triangleRectangleImage = new Mat(img.Size, DepthType.Cv8U, 3)) //用于绘制三角形和矩形的图像
            using (Mat circleImage = new Mat(img.Size, DepthType.Cv8U, 3)) //在其上绘制圆圈的图像
            using (Mat lineImage = new Mat(img.Size, DepthType.Cv8U, 3)) //图像到drtaw线条
            {
                //Convert the image to grayscale and filter out the noise
                CvInvoke.CvtColor(img, gray, ColorConversion.Bgr2Gray);

                //去除噪声
                CvInvoke.GaussianBlur(gray, gray, new System.Drawing.Size(3, 3), 1);

                #region 圆检测
                double cannyThreshold = 180.0;
                double circleAccumulatorThreshold = 120;
                CircleF[] circles = CvInvoke.HoughCircles(gray, HoughModes.Gradient, 2.0, 20.0, cannyThreshold,
                    circleAccumulatorThreshold, 5);
                #endregion

                #region Canny 和边缘检测
                double cannyThresholdLinking = 120.0;
                CvInvoke.Canny(gray, cannyEdges, cannyThreshold, cannyThresholdLinking);
                LineSegment2D[] lines = CvInvoke.HoughLinesP(
                    cannyEdges,
                    1, //Distance resolution in pixel-related units
                    Math.PI / 45.0, //Angle resolution measured in radians.
                    20, //threshold
                    30, //min Line width
                    10); //gap between lines
                #endregion

                #region 查找三角形和矩形
                List<Triangle2DF> triangleList = new List<Triangle2DF>();
                List<RotatedRect> boxList = new List<RotatedRect>(); //a box is a rotated rectangle
                using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
                {
                    CvInvoke.FindContours(cannyEdges, contours, null, RetrType.List,
                        ChainApproxMethod.ChainApproxSimple);
                    int count = contours.Size;
                    for (int i = 0; i < count; i++)
                    {
                        using (VectorOfPoint contour = contours[i])
                        using (VectorOfPoint approxContour = new VectorOfPoint())
                        {
                            CvInvoke.ApproxPolyDP(contour, approxContour, CvInvoke.ArcLength(contour, true) * 0.05,
                                true);
                            if (CvInvoke.ContourArea(approxContour, false) > 250
                            ) //only consider contours with area greater than 250
                            {
                                if (approxContour.Size == 3) //The contour has 3 vertices, it is a triangle
                                {
                                    System.Drawing.Point[] pts = approxContour.ToArray();
                                    triangleList.Add(new Triangle2DF(
                                        pts[0],
                                        pts[1],
                                        pts[2]
                                    ));
                                }
                                else if (approxContour.Size == 4) //The contour has 4 vertices.
                                {
                                    #region determine if all the angles in the contour are within [80, 100] degree
                                    bool isRectangle = true;
                                    System.Drawing.Point[] pts = approxContour.ToArray();
                                    LineSegment2D[] edges =Emgu.CV.PointCollection.PolyLine(pts, true);

                                    for (int j = 0; j < edges.Length; j++)
                                    {
                                        double angle = Math.Abs(
                                            edges[(j + 1) % edges.Length].GetExteriorAngleDegree(edges[j]));
                                        if (angle < 80 || angle > 100)
                                        {
                                            isRectangle = false;
                                            break;
                                        }
                                    }

                                    #endregion

                                    if (isRectangle) boxList.Add(CvInvoke.MinAreaRect(approxContour));
                                }
                            }
                        }
                    }
                }
                #endregion

                #region 绘制三角形和矩形
                triangleRectangleImage.SetTo(new MCvScalar(0));
                foreach (Triangle2DF triangle in triangleList)
                {
                    CvInvoke.Polylines(triangleRectangleImage, Array.ConvertAll(triangle.GetVertices(), System.Drawing.Point.Round),
                        true, new Bgr(System.Drawing.Color.DarkBlue).MCvScalar, 2);
                }

                foreach (RotatedRect box in boxList)
                {
                    CvInvoke.Polylines(triangleRectangleImage, Array.ConvertAll(box.GetVertices(), System.Drawing.Point.Round), true,
                        new Bgr(System.Drawing.Color.DarkOrange).MCvScalar, 2);
                }

                //Drawing a light gray frame around the image
                CvInvoke.Rectangle(triangleRectangleImage,
                    new Rectangle(System.Drawing.Point.Empty,
                        new System.Drawing.Size(triangleRectangleImage.Width - 1, triangleRectangleImage.Height - 1)),
                    new MCvScalar(120, 120, 120));
                //Draw the labels
                CvInvoke.PutText(triangleRectangleImage, "Triangles and Rectangles", new System.Drawing.Point(20, 20),
                    FontFace.HersheyDuplex, 0.5, new MCvScalar(120, 120, 120));
                #endregion

                #region draw 圆
                circleImage.SetTo(new MCvScalar(0));
                foreach (CircleF circle in circles)
                    CvInvoke.Circle(circleImage, System.Drawing.Point.Round(circle.Center), (int)circle.Radius,
                        new Bgr(System.Drawing.Color.Brown).MCvScalar, 2);

                //Drawing a light gray frame around the image
                CvInvoke.Rectangle(circleImage,
                    new Rectangle(System.Drawing.Point.Empty, new System.Drawing.Size(circleImage.Width - 1, circleImage.Height - 1)),
                    new MCvScalar(120, 120, 120));
                //Draw the labels
                CvInvoke.PutText(circleImage, "Circles", new System.Drawing.Point(20, 20), FontFace.HersheyDuplex, 0.5,
                    new MCvScalar(120, 120, 120));
                #endregion

                #region draw lines
                lineImage.SetTo(new MCvScalar(0));
                foreach (LineSegment2D line in lines)
                    CvInvoke.Line(lineImage, line.P1, line.P2, new Bgr(System.Drawing.Color.Green).MCvScalar, 2);
                //Drawing a light gray frame around the image
                CvInvoke.Rectangle(lineImage,
                    new Rectangle(System.Drawing.Point.Empty, new System.Drawing.Size(lineImage.Width - 1, lineImage.Height - 1)),
                    new MCvScalar(120, 120, 120));
                //Draw the labels
                CvInvoke.PutText(lineImage, "Lines", new System.Drawing.Point(20, 20), FontFace.HersheyDuplex, 0.5,
                    new MCvScalar(120, 120, 120));
                #endregion

                Mat result = new Mat();
                CvInvoke.VConcat(new Mat[] { img, triangleRectangleImage, circleImage, lineImage }, result);
                return result;
            }
        }

    }
}
